﻿using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using MyWeb.Common.DB;
using MyWeb.Common.Helper;
using MyWeb.Common.LogHelper;
using Newtonsoft.Json;
using SqlSugar;
using SqlSugar.Extensions;
using StackExchange.Profiling;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyWeb.Extensions.ServiceExtensions.Sqlsugar
{
    /// <summary>
    /// SqlSugar 启动服务
    /// </summary>
    public static class SqlsugarSetup
    {
        private static readonly MemoryCache Cache = new MemoryCache(new MemoryCacheOptions());

        /// <summary>
        /// SqlSugar 启动服务
        /// </summary>
        /// <param name="services"></param>
        /// <exception cref="ArgumentNullException"></exception>
        public static void AddSqlsugarSetup(this IServiceCollection services)
        {
            if (services == null) throw new ArgumentNullException(nameof(services));

            // 默认添加主数据库连接
            MainDb.CurrentDbConnId = AppSettings.app(new string[] { "MainDB" });
            Base64Helper base64 = new Base64Helper();
            // SqlSugarScope是线程安全，可使用单例注入
            services.AddSingleton<ISqlSugarClient>(o =>
            {
                // 连接字符串
                var listConfig = new List<ConnectionConfig>();
                // 从库
                var listConfig_Slave = new List<SlaveConnectionConfig>();
                BaseDBConfig.MutiConnectionString.slaveDbs.ForEach(s =>
                {
                    /// 1.如果存在事务所有操作都走主库
                    /// 2.不存在事务 修改、写入、删除走主库，查询操作走从库
                    /// 3.HitRate 越大走这个从库的概率越大
                    /// 假设A从库 HitRate设置为5
                    /// 假设B从库 HitRate设置为10
                    /// A的概率为33.3 % B的概率为66.6 %
                    listConfig_Slave.Add(new SlaveConnectionConfig()
                    {
                        HitRate = s.HitRate,
                        ConnectionString = base64.Base64Decode(s.Connection)
                    });
                });

                BaseDBConfig.MutiConnectionString.allDbs.ForEach(m =>
                {
                    listConfig.Add(new ConnectionConfig()
                    {
                        ConfigId = UtilExtensions.ObjToString(m.ConnId).ToLower(),
                        ConnectionString = base64.Base64Decode(m.Connection),
                        DbType = (DbType)m.DbType,
                        IsAutoCloseConnection = true,//是否自动关闭连接
                        SlaveConnectionConfigs = listConfig_Slave,//从库 
                        AopEvents = new AopEvents
                        {
                            //SQL执行前
                            OnLogExecuting = (sql, p) =>
                            {
                                //是否启用sql切面编程
                                if (UtilExtensions.ObjToBool(AppSettings.app(new string[] { "AppSettings", "SqlAOP", "Enabled" })))
                                {
                                    //是否AOP日志记录到文件
                                    if (UtilExtensions.ObjToBool(AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToFile", "Enabled" })))
                                    {
                                        Parallel.For(0, 1, e =>
                                        {
                                            MiniProfiler.Current.CustomTiming("SQL：", GetParas(p) + "【SQL语句】：" + sql);
                                            //LogLock.OutSql2Log("SqlLog", new string[] { GetParas(p), "【SQL语句】：" + sql });
                                            LogLock.OutLogAOP("SqlLog", "", new string[] { sql.GetType().ToString(), GetParas(p), "【SQL语句】：" + sql });

                                        });
                                    }
                                    //是否AOP日志打印到页面
                                    if (UtilExtensions.ObjToBool(AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToConsole", "Enabled" })))
                                    {
                                        ConsoleHelper.WriteColorLine(UtilMethods.GetNativeSql(sql, p), ConsoleColor.DarkCyan);
                                    }
                                }
                            },
                            //SQL执行后
                            OnLogExecuted = (sql, p) =>
                            {

                            },
                            //执行前（数据）
                            DataExecuting = (oldValue, entityInfo) =>
                            {
                                //执行插入sql
                                if(entityInfo.OperationType == DataFilterType.InsertByObject)
                                {

                                }

                                //执行修改sql
                                if (entityInfo.OperationType == DataFilterType.UpdateByObject)
                                {
                                    if(entityInfo.EntityName == "UpdateTime")
                                    {
                                        entityInfo.SetValue(DateTime.Now);
                                    }
                                }

                                //执行删除sql
                                if (entityInfo.OperationType == DataFilterType.DeleteByObject)
                                {

                                }
                            },
                            //执行完成之后，记录修改日志
                            OnDiffLogEvent = async it =>
                            {
                                //操作前记录  包含： 字段描述 列名 值 表名 表描述
                                var editBeforeData = it.BeforeData;//插入Before为null，之前还没进库
                                //操作后记录  包含： 字段描述 列名 值  表名 表描述
                                var editAfterData = it.AfterData;
                                var sql = it.Sql;//执行的sql
                                var parameter = it.Parameters;//执行的sql参数
                                var data = it.BusinessData;//这边会显示你传进来的对象
                                var time = (TimeSpan)it.Time;//执行时间
                                var diffType = it.DiffType;//enum insert 、update and delete

                                string str = "";
                                string tableId = "";
                                for (int i = 0; i < editAfterData.Count; i++)
                                {
                                    tableId = "";
                                    var itemAfter = editAfterData[i];
                                    if (itemAfter.TableName != "SysModifyHistory")
                                    {
                                        //循环列
                                        foreach (var dtAfter in itemAfter.Columns)
                                        {
                                            if (dtAfter.IsPrimaryKey)
                                            {
                                                tableId = UtilExtensions.ObjToString(dtAfter.Value);
                                            }

                                            //修改前的数据,插入为null
                                            DiffLogColumnInfo? dtBefore = null;
                                            if (editBeforeData != null)
                                            {
                                                dtBefore = editBeforeData[i].Columns.Where(d => d.ColumnName == dtAfter.ColumnName).First();
                                            }

                                            if (dtBefore != null && UtilExtensions.ObjToString(dtAfter.Value) != UtilExtensions.ObjToString(dtBefore.Value))
                                            {
                                                str = str
                                                    + " "
                                                    + dtAfter.ColumnName
                                                    + "(" + dtAfter.ColumnDescription + "):"
                                                    + UtilExtensions.ObjToString(dtBefore.Value)
                                                    + "-->"
                                                    + UtilExtensions.ObjToString(dtAfter.Value);
                                            }
                                            if (dtBefore == null)//执行插入
                                            {
                                                str = str
                                                    + " "
                                                    + dtAfter.ColumnName
                                                    + "(" + dtAfter.ColumnDescription + "):-->"
                                                    + UtilExtensions.ObjToString(dtAfter.Value);
                                            }
                                        }
                                    }
                                    if (str != "" && !str.IsNullOrEmpty())
                                    {
                                        //在这里可以插入修改记录到数据库 你这个咋用，，类似于记录表数据修改日志，你搞一个示例我看看
                                        TableModifyHistory modifyHistory = new TableModifyHistory()
                                        {
                                            TableName = itemAfter.TableName,
                                            TablePKId = tableId,
                                            ModifyType = diffType.ToString(),
                                            UserName = "",
                                            DiffSql = sql,
                                            DiffParam = "[" + string.Join(",", parameter.Select((SugarParameter it) => $"{{\"Name\":\"{it.ParameterName}\",\"Value\":\"{it.Value}\",\"Type\":\"{it.DbType}\"}}")) + "]",
                                            BusinessData = UtilExtensions.ObjToString(data),
                                            ExecutionTime = time.TotalSeconds,
                                            ModifyDate = DateTime.Now,
                                            ModifyHistory = str
                                        };

                                        //执行插入到数据库
                                        await Db(m.Connection, (DbType)m.DbType).Insertable(modifyHistory).ExecuteReturnSnowflakeIdAsync();
                                        
                                    }
                                }

                            }
                        },
                        MoreSettings = new ConnMoreSettings()
                        {
                            IsAutoRemoveDataCache = true
                        },
                        // 自定义特性
                        ConfigureExternalServices = new ConfigureExternalServices()
                        {
                            DataInfoCacheService = new SqlSugarMemoryCacheService(Cache),
                            EntityService = (property, column) =>
                            {
                                if (column.IsPrimarykey && property.PropertyType == typeof(int))
                                {
                                    column.IsIdentity = true;
                                }
                            }
                        },
                        InitKeyType = InitKeyType.Attribute
                    }
                   );
                });
                return new SqlSugarScope(listConfig);
            });
        }

        /// <summary>
        /// 获取数据库连接
        /// </summary>
        /// <param name="conn"></param>
        /// <returns></returns>
        private static ISqlSugarClient Db(string conn, DbType dbType)
        {

            Base64Helper base64 = new Base64Helper();

            return new SqlSugarClient(new ConnectionConfig()
            {
                ConnectionString = base64.Base64Decode(conn),
                DbType = dbType,
                IsAutoCloseConnection = true
            });
        }

        private static string GetWholeSql(SugarParameter[] paramArr, string sql)
        {
            foreach (var param in paramArr)
            {
                sql = sql.Replace(param.ParameterName, SugarParameterConvertToString(param));
            }

            return sql;
        }
        private static string SugarParameterConvertToString(SugarParameter obj)
        {

            if (obj.DbType == System.Data.DbType.Boolean)
            {
                return UtilExtensions.ObjToBool(obj.Value) ? "1" : "0";
            }
            if (obj.DbType == System.Data.DbType.Int16 || obj.DbType == System.Data.DbType.Int32 || obj.DbType == System.Data.DbType.Int64)
            {
                return UtilExtensions.ObjToString(obj.Value);
            }
            if(obj.DbType == System.Data.DbType.DateTime || obj.DbType == System.Data.DbType.Date || obj.DbType == System.Data.DbType.DateTime2)
            {
                return "'" + UtilExtensions.ObjToDate(obj.Value).ToString("yyyy-MM-dd HH:mm:ss.ffffff") + "'";
            }

            return "'" + UtilExtensions.ObjToString(obj.Value) + "'";
        }
        private static string GetParas(SugarParameter[] pars)
        {
            string key = "【SQL参数】：";
            foreach (var param in pars)
            {
                key += $"{param.ParameterName}:{param.Value}\n";
            }

            return key;
        }
    }
}
